/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openFuyao is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

// Package output 负责格式化输出
package output

import (
	"encoding/csv"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"text/tabwriter"

	"env-check/pkg/config"
)

const paddingSize = 2 // 格式化输出默认填充大小

// Formatter 输出格式化器
type Formatter struct{}

// NewFormatter 创建新的格式化器
func NewFormatter() *Formatter {
	return &Formatter{}
}

// FormatQuery 格式化查询结果
func (f *Formatter) FormatQuery(result *config.QueryResult, format string) (string, error) {
	switch format {
	case "json":
		return f.formatQueryJSON(result)
	case "csv":
		return f.formatQueryCSV(result)
	default:
		return f.formatQueryText(result)
	}
}

// FormatClean 格式化清理结果
func (f *Formatter) FormatClean(result *config.CleanResult, format string) (string, error) {
	switch format {
	case "json":
		return f.formatCleanJSON(result)
	case "csv":
		return f.formatCleanCSV(result)
	default:
		return f.formatCleanText(result)
	}
}

// FormatProgramCheck 格式化程序检查结果
func (f *Formatter) FormatProgramCheck(result *config.ProgramCheckResult, format string) (string, error) {
	switch format {
	case "json":
		return f.formatProgramCheckJSON(result)
	case "csv":
		return f.formatProgramCheckCSV(result)
	default:
		return f.formatProgramCheckText(result)
	}
}

// 查询结果文本格式
func (f *Formatter) formatQueryText(result *config.QueryResult) (string, error) {
	var builder strings.Builder

	builder.WriteString("=== file query result ===\n")
	builder.WriteString(fmt.Sprintf("query time: %s\n\n", result.Timestamp.Format("2006-01-02 15:04:05")))

	writer := tabwriter.NewWriter(&builder, 0, 0, paddingSize, ' ', 0)

	fmt.Fprintln(writer, "Path\tExists\tType\tOwner\tGroup\tPermissions")

	for _, file := range result.Files {
		f.formatFileText(writer, file)
	}

	if err := writer.Flush(); err != nil {
		return "", err
	}

	// 添加汇总信息
	builder.WriteString("\n=== summary ===\n")
	builder.WriteString(fmt.Sprintf("Total Checked: %d\n", result.Summary.TotalChecked))
	builder.WriteString(fmt.Sprintf("Total Exists: %d\n", result.Summary.TotalExists))
	builder.WriteString(fmt.Sprintf("Total Missing: %d\n", result.Summary.TotalMissing))
	builder.WriteString(fmt.Sprintf("Total Dirs: %d\n", result.Summary.TotalDirs))
	builder.WriteString(fmt.Sprintf("Total Files: %d\n", result.Summary.TotalFiles))

	return builder.String(), nil
}

// 清理结果文本格式
func (f *Formatter) formatCleanText(result *config.CleanResult) (string, error) {
	var builder strings.Builder

	builder.WriteString("=== file clean result ===\n")
	builder.WriteString(fmt.Sprintf("clean time: %s\n\n", result.Timestamp.Format("2006-01-02 15:04:05")))

	// 显示删除的文件
	if len(result.Deleted) > 0 {
		builder.WriteString("already delete file:\n")
		writer := tabwriter.NewWriter(&builder, 0, 0, paddingSize, ' ', 0)
		fmt.Fprintln(writer, "path\ttype")
		fmt.Fprintln(writer, "----\t----")
		for _, file := range result.Deleted {
			typeStr := "file"
			if file.IsDir {
				typeStr = "dir"
			}
			fmt.Fprintf(writer, "%s\t%s\n", file.Path, typeStr)
		}
		if err := writer.Flush(); err != nil {
			return "", err
		}
		builder.WriteString("\n")
	}

	// 显示失败的文件
	if len(result.Failed) > 0 {
		builder.WriteString("delete failed:\n")
		for _, file := range result.Failed {
			builder.WriteString(fmt.Sprintf("  %s: %s\n", file.Path, file.Error))
		}
		builder.WriteString("\n")
	}

	// 显示跳过的文件
	if len(result.Skipped) > 0 {
		builder.WriteString("skipped:\n")
		for _, file := range result.Skipped {
			reason := "user canceled"
			if file.Error != "" {
				reason = file.Error
			}
			builder.WriteString(fmt.Sprintf("  %s: %s\n", file.Path, reason))
		}
		builder.WriteString("\n")
	}

	// 添加汇总信息
	builder.WriteString("=== summary ===\n")
	builder.WriteString(fmt.Sprintf("Total Checked: %d\n", result.Summary.TotalChecked))
	builder.WriteString(fmt.Sprintf("Total Deleted: %d\n", result.Summary.TotalDeleted))
	builder.WriteString(fmt.Sprintf("Total Failed: %d\n", result.Summary.TotalFailed))
	builder.WriteString(fmt.Sprintf("Total Skipped: %d\n", result.Summary.TotalSkipped))

	return builder.String(), nil
}

// 程序检查结果文本格式
func (f *Formatter) formatProgramCheckText(result *config.ProgramCheckResult) (string, error) {
	var builder strings.Builder

	builder.WriteString("=== program check result ===\n")
	builder.WriteString(fmt.Sprintf("check time: %s\n\n", result.Timestamp.Format("2006-01-02 15:04:05")))

	writer := tabwriter.NewWriter(&builder, 0, 0, paddingSize, ' ', 0)
	fmt.Fprintln(writer, "name\tinstallation\tversion\tpath")
	fmt.Fprintln(writer, "--------\t--------\t----\t----")

	for _, program := range result.Programs {
		status := "uninstalled"
		if program.Installed {
			status = "installed"
		}

		version := program.Version
		if version == "" {
			version = "unknown"
		}

		path := program.Path
		if path == "" {
			path = "not found"
		}

		fmt.Fprintf(writer, "%s\t%s\t%s\t%s\n",
			program.Name, status, version, path)
	}
	if err := writer.Flush(); err != nil {
		return "", err
	}

	// 添加汇总信息
	builder.WriteString("\n=== summary ===\n")
	builder.WriteString(fmt.Sprintf("Total Checked: %d\n", result.Summary.TotalChecked))
	builder.WriteString(fmt.Sprintf("Total Installed: %d\n", result.Summary.TotalInstalled))
	builder.WriteString(fmt.Sprintf("Total Missing: %d\n", result.Summary.TotalMissing))

	return builder.String(), nil
}

// 工具函数
func (f *Formatter) formatFileText(writer *tabwriter.Writer, file config.FileInfo) {
	status := "missing"
	if file.Exists {
		status = "exists"

		fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%s\t%s\n",
			file.Path, status, f.getTypeString(file), file.Owner, file.Group, file.Permissions)
	} else {
		fmt.Fprintf(writer, "%s\t%s\n", file.Path, status)
	}
}

func (f *Formatter) formatQueryJSON(result *config.QueryResult) (string, error) {
	data, err := json.MarshalIndent(result, "", "  ")
	if err != nil {
		return "", err
	}
	return string(data), nil
}

func (f *Formatter) formatCleanJSON(result *config.CleanResult) (string, error) {
	data, err := json.MarshalIndent(result, "", "  ")
	if err != nil {
		return "", err
	}
	return string(data), nil
}

func (f *Formatter) formatProgramCheckJSON(result *config.ProgramCheckResult) (string, error) {
	data, err := json.MarshalIndent(result, "", "  ")
	if err != nil {
		return "", err
	}
	return string(data), nil
}

func (f *Formatter) formatQueryCSV(result *config.QueryResult) (string, error) {
	var builder strings.Builder
	writer := csv.NewWriter(&builder)

	err := writer.Write([]string{"Path", "Exists", "Type", "Owner", "Group", "Permissions", "Error"})
	if err != nil {
		return "", err
	}
	for _, file := range result.Files {
		err := writer.Write([]string{
			file.Path,
			strconv.FormatBool(file.Exists),
			f.getTypeString(file),
			file.Owner,
			file.Group,
			file.Permissions,
			file.Error,
		})
		if err != nil {
			return "", err
		}
	}
	showQueryTotal(result)
	writer.Flush()
	return builder.String(), nil
}

func (f *Formatter) formatCleanCSV(result *config.CleanResult) (string, error) {
	var builder strings.Builder
	writer := csv.NewWriter(&builder)

	err := writer.Write([]string{"Path", "Status", "Type", "Error"})
	if err != nil {
		return "", err
	}
	for _, file := range result.Deleted {
		err := writer.Write([]string{
			file.Path,
			"deleted",
			f.getTypeString(file),
			"",
		})
		if err != nil {
			return "", err
		}
	}
	for _, file := range result.Failed {
		err := writer.Write([]string{
			file.Path,
			"failed",
			f.getTypeString(file),
			file.Error,
		})
		if err != nil {
			return "", err
		}
	}
	for _, file := range result.Skipped {
		err := writer.Write([]string{
			file.Path,
			"skipped",
			f.getTypeString(file),
			file.Error,
		})
		if err != nil {
			return "", err
		}
	}
	showCleanTotal(result)
	writer.Flush()
	return builder.String(), nil
}

func (f *Formatter) formatProgramCheckCSV(result *config.ProgramCheckResult) (string, error) {
	var builder strings.Builder
	writer := csv.NewWriter(&builder)

	err := writer.Write([]string{"Program", "Installed", "Version", "Path", "Error"})
	if err != nil {
		return "", err
	}
	for _, program := range result.Programs {
		err := writer.Write([]string{
			program.Name,
			strconv.FormatBool(program.Installed),
			program.Version,
			program.Path,
			program.Error,
		})
		if err != nil {
			return "", err
		}
	}
	showProgramCheckTotal(result)
	writer.Flush()
	return builder.String(), nil
}

func (f *Formatter) getTypeString(file config.FileInfo) string {
	if !file.Exists {
		return "missing"
	}
	if file.IsDir {
		return "directory"
	}
	return "file"
}

func showQueryTotal(result *config.QueryResult) {
	fmt.Println("=== summary ===")
	fmt.Print(fmt.Sprintf("Total Checked: %d\n", result.Summary.TotalChecked))
	fmt.Print(fmt.Sprintf("Total Exists: %d\n", result.Summary.TotalExists))
	fmt.Print(fmt.Sprintf("Total Missing: %d\n", result.Summary.TotalMissing))
	fmt.Print(fmt.Sprintf("Total Dirs: %d\n", result.Summary.TotalDirs))
	fmt.Print(fmt.Sprintf("Total Files: %d\n", result.Summary.TotalFiles))
}

func showCleanTotal(result *config.CleanResult) {
	fmt.Println("=== summary ===")
	fmt.Print(fmt.Sprintf("Total Checked: %d\n", result.Summary.TotalChecked))
	fmt.Print(fmt.Sprintf("Total Deleted: %d\n", result.Summary.TotalDeleted))
	fmt.Print(fmt.Sprintf("Total Failed: %d\n", result.Summary.TotalFailed))
	fmt.Print(fmt.Sprintf("Total Skipped: %d\n", result.Summary.TotalSkipped))
}

func showProgramCheckTotal(result *config.ProgramCheckResult) {
	fmt.Println("=== summary ===")
	fmt.Print(fmt.Sprintf("Total Checked: %d\n", result.Summary.TotalChecked))
	fmt.Print(fmt.Sprintf("Total Installed: %d\n", result.Summary.TotalInstalled))
	fmt.Print(fmt.Sprintf("Total Missing: %d\n", result.Summary.TotalMissing))
}
